#pragma once

#include <string>
#include <vector>

#include "XmlConfig.h"

#define MAX_XML_CONF_CDATA_CHARS	256
#define CONFIG_TRUE_STRING			L"true"
#define CONFIG_FALSE_STRING			L"false"

using std::string;
using std::wstring;
using std::vector;

//A Wrapper vector for Config Element Pointer
typedef struct _ConfigPointerVector {

	typedef struct _ConfigPointerWrapper {
		enum {
			TYPE_ENTRY = 0,
			TYPE_SECTION,
			TYPE_GROUP
		} PtType;

		union {
			CXmlConfig::ConfigEntry* pEntry;
			CXmlConfig::ConfigSection* pSection;
			CXmlConfig::ConfigGroup* pGroup;
		} ConfigPointer;

	} ConfigPointerWrapper;

	vector<ConfigPointerWrapper> m_vector;

	void AddEntry(CXmlConfig::ConfigEntry* pEntry)
	{
		ConfigPointerWrapper pw;
		pw.PtType = ConfigPointerWrapper::TYPE_ENTRY;
		pw.ConfigPointer.pEntry = pEntry;

		m_vector.push_back(pw);
	}

	void AddSection(CXmlConfig::ConfigSection* pSection)
	{
		ConfigPointerWrapper pw;
		pw.PtType = ConfigPointerWrapper::TYPE_SECTION;
		pw.ConfigPointer.pSection = pSection;

		m_vector.push_back(pw);	
	}

	void AddGroup(CXmlConfig::ConfigGroup* pGroup)
	{
		ConfigPointerWrapper pw;
		pw.PtType = ConfigPointerWrapper::TYPE_GROUP;
		pw.ConfigPointer.pGroup = pGroup;

		m_vector.push_back(pw);	
	}

	~_ConfigPointerVector()
	{
		for(vector<ConfigPointerWrapper>::iterator it = m_vector.begin(); it != m_vector.end(); ++it)
		{
			switch(it->PtType)
			{
			case ConfigPointerWrapper::TYPE_ENTRY:

				delete it->ConfigPointer.pEntry;
				break;

			case ConfigPointerWrapper::TYPE_SECTION:
				
				delete it->ConfigPointer.pSection;
				break;

			case ConfigPointerWrapper::TYPE_GROUP:

				delete it->ConfigPointer.pGroup;
				break;
			}
		}
	}

} ConfigPointerVector;

//Wrapper for bool value in config to convert bool to string
class boolConfWrapper
{
public:
	bool bValue;
	wstring wstrValue;

	boolConfWrapper& operator=(const bool bParam)
	{
		bValue = bParam;
		return *this;
	}

	operator bool() const { return bValue; }
	operator bool&() { return bValue; }
	operator LPCWSTR()
	{
		if(bValue)
			wstrValue = CONFIG_TRUE_STRING;
		else
			wstrValue = CONFIG_FALSE_STRING;

		return wstrValue.c_str();
	}
};

//Wrapper for long value in config to convert long to string
class LONGConfWrapper
{
public:
	LONG lValue;
	wstring wstrValue;

	LONGConfWrapper& operator=(const LONG lParam)
	{
		lValue = lParam;
		return *this;
	}

	operator LONG() const { return lValue; }
	operator LONG&() { return lValue; }
	operator LPCWSTR()
	{
		WCHAR pwstrLong[16] = { 0 };

		::_ltow(lValue, pwstrLong, 10);
		wstrValue = pwstrLong;

		return wstrValue.c_str();
	}
};

//Wrapper for UINT value in config to convert UINT to string
class UINTConfWrapper
{
public:
	UINT uiValue;
	wstring wstrValue;

	UINTConfWrapper& operator=(const UINT uiParam)
	{
		uiValue = uiParam;
		return *this;
	}

	operator UINT() const { return uiValue; }
	operator UINT&() { return uiValue; }
	operator LPCWSTR()
	{
		WCHAR pwstrUINT[16] = { 0 };

		::_itow(uiValue, pwstrUINT, 10);
		wstrValue = pwstrUINT;

		return wstrValue.c_str();
	}
};

//Wrapper for string value in config to convert string to wstring
class stringConfWrapper
{
public:
	string strValue;
	wstring wstrValue;

	stringConfWrapper& operator=(const string strParam)
	{
		strValue = strParam;
		return *this;
	}

	stringConfWrapper& operator=(const char* pcParam)
	{
		strValue = pcParam;
		return *this;
	}

	operator string() const { return strValue; }
	operator string&() { return strValue; }
	operator LPCWSTR()
	{
		WCHAR pwstrWstring[512] = { 0 };

		::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strValue.c_str(), -1, pwstrWstring, 512);
		wstrValue = pwstrWstring;

		return wstrValue.c_str();
	}

	const char* c_str() const
	{
		return strValue.c_str();
	} 
};

class CTARConfigs
{
public:
	CTARConfigs();
	~CTARConfigs();

public:
	HRESULT LoadConfigFromXML();

	static HRESULT LoadBoolValue(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, bool& bValue);
	static HRESULT LoadStringValueW(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, wstring& wstrValue, bool bIsCDATA = false);
	static HRESULT LoadStringValueA(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, string& strValue, bool bIsCDATA = false);
	static HRESULT LoadLongValue(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, LONG& lValue);
	static HRESULT LoadUINTValue(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, UINT& uiValue);
	static HRESULT LoadStringVectorW(CLiteXMLPhaser* pPhaser, LPCWSTR lpcwstrPath, LPCWSTR lpcwstrPropery, vector<wstring>& vecwstrValue, bool bIsCDATA = false);

public:

	//App Settings
	struct _AppSettings {
	wstring wstrXMLFilePath;

	boolConfWrapper bIsUserDrag;	
	boolConfWrapper bIsInitSelectAll;

	LONGConfWrapper lJSConnectionTimeOut;
	LONGConfWrapper lJSTransferTimeOut;
	UINTConfWrapper uiJSItemSnifferTimeWait;
	stringConfWrapper strJSUserAgent;

	UINTConfWrapper uiDefDownThread;
	wstring			wstrDefSavePath;
	boolConfWrapper bIsStartNow;

	boolConfWrapper bIsDefSendFlash;
	boolConfWrapper bIsDefExpHttp;
	boolConfWrapper bIsDefEncGB;
	wstring			wstrListSavePath;

	_AppSettings()
	{
		wstrXMLFilePath = L"TASettings.xml";

		bIsUserDrag = false;
		bIsInitSelectAll = true;

		uiJSItemSnifferTimeWait = 2000;
		lJSConnectionTimeOut = 15;
		lJSTransferTimeOut = 20;

		uiDefDownThread = 2;
		bIsStartNow = true;
		
		bIsDefSendFlash = true;
		bIsDefExpHttp = true;
		bIsDefEncGB = true;
	}

	HRESULT LoadSettings()
	{
		HRESULT hr = S_OK;
		CLiteXMLPhaser* pAppSettings = new CLiteXMLPhaser(wstrXMLFilePath.c_str(), CLiteXMLPhaser::READ_MODE);

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		//Item Select
		//Userdrag mode
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/ItemsSelectDiag/Option", L"/id/IsUserDrag", bIsUserDrag);

		//Is Select all items on init
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/ItemsSelectDiag/Option", L"/id/IsInitSelectAll", bIsInitSelectAll);

		////////////////////////////////////////////////////////////////////////////////////////////////////
		//Export Links
		//Is Select Send Flash
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/LinkExportDiag/Option", L"/id/DefSendFlashRadio", bIsDefSendFlash);

		//Is Export http
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/LinkExportDiag/Option", L"/id/DefExpHttpRadio", bIsDefExpHttp);

		//Is Export Dec GB
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/LinkExportDiag/Option", L"/id/DefEncGB", bIsDefEncGB);

		//Default List Save Path
		hr |= hr;
		LoadStringValueW(pAppSettings, L"/Settings/LinkExportDiag/Option", L"/id/SavePath", wstrListSavePath);

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		//Down Property Diag
		//Default Download thread
		hr |= hr;
		LoadUINTValue(pAppSettings, L"/Settings/DownPropertyDiag/Option", L"/id/DefDownThread", uiDefDownThread);

		//Default Save Path
		hr |= hr;
		LoadStringValueW(pAppSettings, L"/Settings/DownPropertyDiag/Option", L"/id/SavePath", wstrDefSavePath);

		//Is Start Down Now
		hr |= hr;
		LoadBoolValue(pAppSettings, L"/Settings/DownPropertyDiag/Option", L"/id/IsStartNow", bIsStartNow);

		/////////////////////////////////////////////////////////////////////////////////////////////////////
		//JS Settings
		//Get time wait for get next item
		hr |= hr;
		LoadUINTValue(pAppSettings, L"/Settings/JS/TimeSet/Option", L"/id/ItemRequestSleep", uiJSItemSnifferTimeWait);

		//Get Connection time out
		hr |= hr;
		LoadLongValue(pAppSettings, L"/Settings/JS/TimeSet/Option", L"/id/ConnectionTimeOut", lJSConnectionTimeOut);

		//Get transfer time out
		hr |= hr;
		LoadLongValue(pAppSettings, L"/Settings/JS/TimeSet/Option", L"/id/TransferTimeOut", lJSTransferTimeOut);

		//Get Useragent
		hr |= hr;
		LoadStringValueA(pAppSettings, L"/Settings/JS/UserAgent/List", L"/type/1", strJSUserAgent);

		
		//Clear heap
		delete pAppSettings;

		return hr;
	}

	HRESULT SaveSettings()
	{
		HRESULT hr = S_OK;
		ConfigPointerVector configVector;

		//When We Write, we use XMLConfig Class
		CXmlConfig AppConfig(wstrXMLFilePath.c_str());

		AppConfig.SetRootNodeName(L"Settings");

		//ItemsSelectDiag Section
		CXmlConfig::ConfigGroup* pGroup;
		CXmlConfig::ConfigGroup* pSiblingGroup;
		CXmlConfig::ConfigGroup* pOuterGroup;
		CXmlConfig::ConfigSection* pSection;
		CXmlConfig::ConfigEntry* pEntry;

		//////////////////////////////////////////////////////////////////////////////////////////////
		//Download Items Select Diag
		pSection = AppConfig.CreateConfigSection(L"ItemsSelectDiag", L"Option", false, &pGroup);
		configVector.AddSection(pSection);
		configVector.AddGroup(pGroup);

			pEntry = pSection->CreateConfigEntry(bIsUserDrag);
			pEntry->AddAttribute(L"id", L"IsUserDrag");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(bIsInitSelectAll);
			pEntry->AddAttribute(L"id", L"IsInitSelectAll");
			configVector.AddEntry(pEntry);

		AppConfig.AddToRootGroup(pGroup);

		//////////////////////////////////////////////////////////////////////////////////////////////		
		//Link Export Diag
		pSection = AppConfig.CreateConfigSection(L"LinkExportDiag", L"Option", false, &pGroup);
		configVector.AddSection(pSection);
		configVector.AddGroup(pGroup);

			pEntry = pSection->CreateConfigEntry(bIsDefSendFlash);
			pEntry->AddAttribute(L"id", L"DefSendFlashRadio");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(bIsDefExpHttp);
			pEntry->AddAttribute(L"id", L"DefExpHttpRadio");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(bIsDefEncGB);
			pEntry->AddAttribute(L"id", L"DefEncGB");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(wstrListSavePath.c_str());
			pEntry->AddAttribute(L"id", L"SavePath");
			configVector.AddEntry(pEntry);

		AppConfig.AddToRootGroup(pGroup);

		//////////////////////////////////////////////////////////////////////////////////////////////		
		//Down Property Diag
		pSection = AppConfig.CreateConfigSection(L"DownPropertyDiag", L"Option", false, &pGroup);
		configVector.AddSection(pSection);
		configVector.AddGroup(pGroup);

			pEntry = pSection->CreateConfigEntry(uiDefDownThread);
			pEntry->AddAttribute(L"id", L"DefDownThread");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(wstrDefSavePath.c_str());
			pEntry->AddAttribute(L"id", L"SavePath");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(bIsStartNow);
			pEntry->AddAttribute(L"id", L"IsStartNow");
			configVector.AddEntry(pEntry);

		AppConfig.AddToRootGroup(pGroup);

		/////////////////////////////////////////////////////////////////////////////////////////////////
		//JS Settings Diag
		pOuterGroup = AppConfig.CreateConfigGroup(L"JS");
		configVector.AddGroup(pOuterGroup);

		//Time Section
		pSection = AppConfig.CreateConfigSection(L"TimeSet", L"Option", false, &pGroup);
		pOuterGroup->SetSonGroup(pGroup);

		configVector.AddSection(pSection);
		configVector.AddGroup(pGroup);

			pEntry = pSection->CreateConfigEntry(uiJSItemSnifferTimeWait);
			pEntry->AddAttribute(L"id", L"ItemRequestSleep");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(lJSConnectionTimeOut);
			pEntry->AddAttribute(L"id", L"ConnectionTimeOut");
			configVector.AddEntry(pEntry);

			pEntry = pSection->CreateConfigEntry(lJSTransferTimeOut);
			pEntry->AddAttribute(L"id", L"TransferTimeOut");
			configVector.AddEntry(pEntry);

		//User Agent Section
		pSection = AppConfig.CreateConfigSection(L"UserAgent", L"List", false, &pSiblingGroup);
		pGroup->SetSiblingGroup(pSiblingGroup);
		pGroup = pSiblingGroup;
		
		configVector.AddSection(pSection);
		configVector.AddGroup(pGroup);

			pEntry = pSection->CreateConfigEntry(strJSUserAgent);
			pEntry->AddAttribute(L"type", L"1");
			pEntry->AddAttribute(L"name", L"XP, FF3");
			configVector.AddEntry(pEntry);

		AppConfig.AddToRootGroup(pOuterGroup);

		//Flush the configs to disk
		AppConfig.FlushConfigsToDisk();

		//The Config Element heap is release by ConfigVector, so do't worry about heap leak
		//And the xml file will release by the CXmlConfig Destructor
		return hr;
	}

	} AppSettings;

	//JShare Settings, Lucky! These settings have no need to save, so we do not need to provide a save method
	struct _JSSettings {
	wstring wstrXMLFilePath;

	vector<wstring> vecwstrJSURLPatterns;

	wstring wstrJSBBCodeOuterRegex;
	wstring wstrJSBBCodeInnerRegex;
	wstring wstrJSHTMLOuterRegex;
	wstring wstrJSHTMLInnerRegex;
	wstring wstrJSSecIPRegex;
	wstring wstrJSCaptchaKeyWord;
	wstring wstrJSCaptchaLink;
	wstring wstrJSPageRouterLink;
	wstring wstrJSPageFileLink;

	string strJSCookieFilePath;

	_JSSettings()
	{
		wstrXMLFilePath = L"SitePatterns\\JSConfig.xml";
	}

	HRESULT LoadSettings()
	{
		HRESULT hr = S_OK;
		CLiteXMLPhaser* pJSPatternPhaser = new CLiteXMLPhaser(wstrXMLFilePath.c_str(), CLiteXMLPhaser::READ_MODE);

		//Get URL Patterns
		hr |= hr;
		hr = LoadStringVectorW(pJSPatternPhaser, L"/JSConfig/URLPatterns/URL", L"/type/BlogPost", vecwstrJSURLPatterns, true);

		//Get File Info patterns
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/HTML/level/1", wstrJSHTMLOuterRegex, true);

		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/HTML/level/2", wstrJSHTMLInnerRegex, true);

		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/BB/level/1", wstrJSBBCodeOuterRegex, true);

		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/BB/level/2", wstrJSBBCodeInnerRegex, true);

		//Get SecIP patterns
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/SecIP", wstrJSSecIPRegex, true);

		//Get Captcha Keyword
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/CaptchaKeyWord", wstrJSCaptchaKeyWord, true);

		//Get Captcha Link Pattern
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/CaptchaImageLink", wstrJSCaptchaLink, true);

		//Get Page Download Router Link
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/PageRouterLink", wstrJSPageRouterLink, true);

		//Get Page Download File Link
		hr |= hr;
		LoadStringValueW(pJSPatternPhaser, L"/JSConfig/ListItemPatterns/PostList", L"/style/PageFileLink", wstrJSPageFileLink, true);

		//Get cookie file path
		hr |= hr;
		LoadStringValueA(pJSPatternPhaser, L"/JSConfig/Miscellaneous/Value", L"/id/CookieFile", strJSCookieFilePath);


		//Clear Heap
		delete pJSPatternPhaser;

		return hr;
	}

	} JSSettings;
};
